//
//  AliMNSCommon.m
//  AlibabaCloudMNSiOS
//
//  Created by David Chen on 28/03/2017.
//  Copyright © 2017 AliCloud. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonDigest.h>
#import <CommonCrypto/CommonCryptor.h>
#import "CommonCrypto/CommonHMAC.h"
#import "AliMNSCommon.h"

int32_t const CHUNK_SIZE = 8 * 1024;

@implementation AliMNSCommon

@synthesize QUEUES;
@synthesize MESSAGES;
@synthesize TOPICS;
@synthesize SUBSCRIPTIONS;
@synthesize DATE_FORMATTOR;
@synthesize XMLNS;
@synthesize XML_VERSION_STR;

-(instancetype) init
{
    self = [super init];
    if (self != nil)
    {
        QUEUES = @"queues";
        MESSAGES = @"messages";
        TOPICS = @"topics";
        SUBSCRIPTIONS = @"subscriptions";
        DATE_FORMATTOR = @"EEE, dd MMM yyyy HH:mm:ss z";
        XMLNS = @"http://mns.aliyuncs.com/doc/v1/";
        XML_VERSION_STR =@"<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    }
    return self;
}

+ (AliMNSCommon*) getInstance
{
    static AliMNSCommon * singleton;

    @synchronized(self)
    {
        if (!singleton)
        {
            singleton = [[AliMNSCommon alloc] init];
        }

        return singleton;
    }
}

+ (NSString *)base64Md5ForData:(NSData *)data {
    uint8_t * bytes = (uint8_t *)[[AliMNSCommon calcMD5:data] bytes];
    return [self base64ForData:bytes length:CC_MD5_DIGEST_LENGTH];
}

+(NSData*)calcMD5:(NSData*) data
{
    if (data == nil)
    {
        return nil;
    }
    CC_MD5_CTX md5;
    CC_MD5_Init(&md5);
    for (int i = 0; i < data.length; i += CHUNK_SIZE) {
        NSData *subdata = nil;
        if (i <= ((long)data.length - CHUNK_SIZE)) {
            subdata = [data subdataWithRange:NSMakeRange(i, CHUNK_SIZE)];
            CC_MD5_Update(&md5, [subdata bytes], (CC_LONG)[subdata length]);
        } else {
            subdata = [data subdataWithRange:NSMakeRange(i, data.length - i)];
            CC_MD5_Update(&md5, [subdata bytes], (CC_LONG)[subdata length]);
        }
    }
    unsigned char digestResult[CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
    CC_MD5_Final(digestResult, &md5);
    return [NSData dataWithBytes:(const void *)digestResult length:CC_MD5_DIGEST_LENGTH * sizeof(unsigned char)];
}

+ (NSString*)base64ForData:(uint8_t *)input length:(int32_t)length {
    if (input == nil) {
        return nil;
    }
    NSData * data = [NSData dataWithBytes:input length:length];
    return [data base64EncodedStringWithOptions:0];
}

+ (NSString*) getDateString:(NSString*) formatterStr
               withTimezone:(NSString*) timeZone
{
    NSDate* date = [NSDate new];
    NSDateFormatter *dateFormatter = [NSDateFormatter new];
    dateFormatter.timeZone = [NSTimeZone timeZoneWithName:timeZone];
    dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US"];
    dateFormatter.dateFormat = formatterStr;

    return [dateFormatter stringFromDate:date];
}


+ (NSString*)calBase64WithData:(uint8_t *)data {
    static char b[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    NSInteger a = 20;
    NSMutableData* c = [NSMutableData dataWithLength:((a + 2) / 3) * 4];
    uint8_t* d = (uint8_t*)c.mutableBytes;
    NSInteger i;
    for (i=0; i < a; i += 3) {
        NSInteger e = 0;
        NSInteger j;
        for (j = i; j < (i + 3); j++) {
            e <<= 8;
            if (j < a) {
                e |= (0xFF & data[j]);
            }
        }
        NSInteger index = (i / 3) * 4;
        d[index + 0] = b[(e >> 18) & 0x3F];
        d[index + 1] = b[(e >> 12) & 0x3F];
        if ((i + 1) < a) {
            d[index + 2] = b[(e >> 6) & 0x3F];
        } else {
            d[index + 2] = '=';
        }
        if ((i + 2) < a) {
            d[index + 3] = b[(e >> 0) & 0x3F];
        } else {
            d[index + 3] = '=';
        }
    }
    NSString *result = [[NSString alloc] initWithData:c encoding:NSASCIIStringEncoding];
    return result;
}

+ (NSString *)calBase64Sha1WithData:(NSString *)data withSecret:(NSString *)key {
    NSData *secretData = [key dataUsingEncoding:NSUTF8StringEncoding];
    NSData *clearTextData = [data dataUsingEncoding:NSUTF8StringEncoding];
    uint8_t input[20];
    CCHmac(kCCHmacAlgSHA1, [secretData bytes], [secretData length], [clearTextData bytes], [clearTextData length], input);

    return [AliMNSCommon calBase64WithData:input];
}

+ (NSString*) getSignature:(NSMutableDictionary*) headers
                withMethod:(NSString*)method
                   withURI:(NSString*)uri
             withAccessId:(NSString*) aId
             withAccessKey:(NSString*) aKey
{
    NSString* contentMD5 = [headers objectForKey:@"content-md5"];
    NSString* contentType = [headers objectForKey:@"content-type"];
    if(contentMD5 == NULL)
    {
        contentMD5 = @"";
    }

    if(contentType == NULL)
    {
        contentType = @"";
    }

    NSString* dateStr = [headers objectForKey:@"date"];
    NSMutableString* xmnsStrToSign = [NSMutableString new];
    NSArray* keys = [[headers allKeys] sortedArrayUsingSelector:@selector(compare:)];
    NSUInteger count = keys.count;
    for (int i = 0; i < count; i++)
    {
        NSString* key = [keys objectAtIndex:i];
        if ([ key hasPrefix:@"x-mns-"])
        {
            NSString* value = [headers objectForKey:key];
            [xmnsStrToSign appendString:[NSString stringWithFormat:@"%@:%@\n", key, value]];
        }
    }
    NSString* strToSign = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n%@%@", method, contentMD5, contentType, dateStr, xmnsStrToSign, uri];

    NSString * sign = [AliMNSCommon calBase64Sha1WithData:strToSign withSecret:aKey];
    return [NSString stringWithFormat:@"MNS %@:%@", aId, sign];
}


@end
